Conversation
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
|
Note Reviews pausedIt looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the Use the following commands to manage reviews:
Use the checkboxes below for quick actions:
📝 WalkthroughSummary by CodeRabbit
Walkthrough관리자용 과제 관리 페이지 추가 및 API/타입/쿼리 리팩터: Zod 스키마 기반 API 응답 검증 도입, React Query 쿼리·뮤테이션 옵션 중앙화, Zustand 세션 스토어 추가, 도메인 타입을 z.infer로 전환, 과제/단위/과정 관련 컴포넌트·페이지의 선택/삭제/편집 흐름 변경. (요약 50단어 이내) Changes
Sequence Diagram(s)sequenceDiagram
participant User as User
participant Browser as Browser(UI)
participant Page as AssignmentManagePage
participant Q as ReactQuery/QueryClient
participant API as assignmentApi
participant Server as Backend
rect rgba(0,128,255,0.5)
User->>Browser: 삭제 버튼 클릭
Browser->>Page: onDelete(id)
Page->>Browser: confirm() (사용자 확인)
end
Page->>API: deleteAssignment(id)
API->>Server: DELETE /assignments/{id}
Server-->>API: 200 {success,response:"..."}
API-->>Page: parsed response (apiResponseSchema)
Page->>Q: invalidateQueries(assignmentQueries.getAllAssignments().queryKey)
Page->>Q: invalidateQueries(assignmentQueries.getAssignmentsByCourse(courseId).queryKey)
Q-->>Page: refetch triggered
Page-->>Browser: alert("삭제 완료")
Estimated code review effort🎯 4 (Complex) | ⏱️ ~45 minutes Possibly related PRs
Suggested labels
Suggested reviewers
추가 제안(짧게): API 응답 파싱 범위가 넓어졌으니 주요 엔드포인트에 대해 Zod parse 성공/실패 케이스의 통합 테스트나 E2E를 권장합니다. 참고: https://zod.dev/, https://tanstack.com/query/latest/docs/react/overview 짧은 칭찬 — 큰 리팩터, 깔끔해요! 🚥 Pre-merge checks | ✅ 3✅ Passed checks (3 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches
🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Actionable comments posted: 6
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
src/pages/unit-editor/ui/UnitForm.tsx (1)
66-73:⚠️ Potential issue | 🟠 Major콜백이 동기 함수이므로
isSubmitting보호가 조기에 해제되어 중복 제출 위험이 있습니다.
onCreateUnit과onUpdateUnit은void를 반환하므로, 현재의 제안된 수정안(async/await)은 타입 시스템상 불가능합니다. 대신useMutation의mutateAsync를 활용해야 합니다:
UnitEditorPage.tsx의 콜백을 다음과 같이 변경:const onCreateUnit = (unitForm: TUnitFormSchema) => addUnit({courseId, unitForm}); // mutate 사용 시 Promise 반환 불가→
const onCreateUnit = async (unitForm: TUnitFormSchema) => await addUnit({courseId, unitForm}); // mutateAsync 사용 필요
UnitFormProps타입 정의를 업데이트:onCreateUnit: (unit: TUnitFormSchema) => Promise<void>; onUpdateUnit: (unitId: number, unit: TUnitFormSchema) => Promise<void>;
UnitForm.tsx의onSubmit을 async로 변경:const onSubmit = async (data: TUnitFormSchema) => { if (mode === 'editing' && unit) { await onUpdateUnit(unit.id, data); return; } await onCreateUnit(data); };이렇게 하면
handleSubmit이 콜백의 Promise를 인식하여 mutation 완료까지isSubmitting을 유지하게 됩니다. TanStack Query 공식 문서의 "Using Mutations" 섹션을 참고하세요.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/pages/unit-editor/ui/UnitForm.tsx` around lines 66 - 73, The submit handler releases isSubmitting too early because onCreateUnit/onUpdateUnit are currently synchronous (void); update UnitEditorPage.tsx to call the TanStack Query mutateAsync (e.g., await addUnit.mutateAsync({...}) / await updateUnit.mutateAsync(...)) and change the exported UnitFormProps so onCreateUnit: (unit: TUnitFormSchema) => Promise<void> and onUpdateUnit: (unitId: number, unit: TUnitFormSchema) => Promise<void>; then make UnitForm.tsx's onSubmit async and await the calls to onCreateUnit/onUpdateUnit (refer to onSubmit, UnitFormProps, onCreateUnit/onUpdateUnit, and addUnit/updateUnit mutateAsync) so handleSubmit sees the Promise and keeps isSubmitting until mutation completes.
🧹 Nitpick comments (16)
src/entities/auth/api/authApi.ts (1)
3-3: 임포트 경로는 절대 경로(@/...)로 통일해 주세요Line 3의 상대 경로(
../model/schemas)는 현재 프로젝트 컨벤션과 어긋납니다. 작은 차이지만 리팩토링/이동 시 경로 안정성이 크게 달라집니다.빠른 수정안
-import {kakaoLoginResponseSchema} from '../model/schemas'; +import {kakaoLoginResponseSchema} from '@/entities/auth/model/schemas';As per coding guidelines
절대 경로 임포트(@/...) 사용.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/entities/auth/api/authApi.ts` at line 3, The import of kakaoLoginResponseSchema in authApi.ts uses a relative path; change the import to use the project's absolute alias (e.g. replace "../model/schemas" with an absolute import like "@/entities/auth/model/schemas") so the kakaoLoginResponseSchema import follows the `@/`... convention used across the codebase and remains stable during refactors.src/pages/unit-editor/model/types.ts (1)
10-13:mode: Mode | null도입에 맞춰UnitFormProps를 판별 유니온으로 분리해 주세요.현재 구조는
mode값과 무관하게unitIndex/핸들러 조합이 모두 허용되어, 런타임 분기 의존도가 커집니다. 모드별 props를 타입으로 강제하면 안전성이 더 올라갑니다.🔧 제안 diff
-export interface UnitFormProps { - unit?: Unit; - unitIndex: number; - mode: Mode | null; - onCreateUnit: (unit: TUnitFormSchema) => void; - onUpdateUnit: (unitId: number, unit: TUnitFormSchema) => void; - onDeleteUnit: (unitId: number) => void; -} +type UnitFormBaseProps = { + onDeleteUnit: (unitId: number) => void; +}; + +type CreatingUnitFormProps = UnitFormBaseProps & { + mode: 'creating'; + unitIndex: number; + unit?: never; + onCreateUnit: (unit: TUnitFormSchema) => void; + onUpdateUnit?: never; +}; + +type EditingUnitFormProps = UnitFormBaseProps & { + mode: 'editing'; + unit: Unit; + unitIndex: number; + onUpdateUnit: (unitId: number, unit: TUnitFormSchema) => void; + onCreateUnit?: never; +}; + +type InactiveUnitFormProps = UnitFormBaseProps & { + mode: null; + unit?: never; + unitIndex?: never; + onCreateUnit?: never; + onUpdateUnit?: never; +}; + +export type UnitFormProps = + | CreatingUnitFormProps + | EditingUnitFormProps + | InactiveUnitFormProps;공식 문서 참고: https://www.typescriptlang.org/docs/handbook/2/narrowing.html#discriminated-unions
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/pages/unit-editor/model/types.ts` around lines 10 - 13, UnitFormProps currently allows any combination of mode, unitIndex and handlers which weakens type safety; change it to a discriminated union keyed on mode (the Mode type) by creating separate interfaces (e.g., UnitFormCreateProps with mode: 'create' and required onCreateUnit only; UnitFormEditProps with mode: 'edit', unitIndex: number and required onUpdateUnit; UnitFormDeleteProps with mode: 'delete', unitIndex: number and required onDeleteUnit) and export type UnitFormProps = UnitFormCreateProps | UnitFormEditProps | UnitFormDeleteProps; ensure you reference the exact symbols TUnitFormSchema, Mode, unitIndex, onCreateUnit, onUpdateUnit and onDeleteUnit when renaming/organizing so callers and components are updated to match the new discriminated union shape.src/entities/assignment/model/schemas.ts (2)
13-19: 중첩 스키마 추출 고려
assignments배열 내부의 객체 스키마가 인라인으로 정의되어 있습니다. 다른 곳에서 재사용 가능성이 있다면 별도 스키마로 추출하는 것도 좋습니다.♻️ 중첩 스키마 추출 예시
+export const assignmentItemSchema = z.object({ + course: z.string(), + section: z.string(), + assignment: z.string(), +}); + export const assignmentScheduleSchema = z.object({ date: z.string(), remainingDays: z.number(), - assignments: z.array( - z.object({ - course: z.string(), - section: z.string(), - assignment: z.string(), - }) - ), + assignments: z.array(assignmentItemSchema), });🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/entities/assignment/model/schemas.ts` around lines 13 - 19, The inline object schema used inside the assignments array should be extracted into a reusable named schema: create a const like AssignmentItemSchema (or AssignmentSchema) defined as z.object({ course: z.string(), section: z.string(), assignment: z.string() }) and then replace the inline z.object(...) in assignments with z.array(AssignmentItemSchema); export the new schema if it will be reused elsewhere and update any type aliases or imports that should reference AssignmentItemSchema instead of the inline object.
2-2: Zod import 스타일 일관성
import z from 'zod'(default import)를 사용하고 있는데,src/shared/model/schemas.ts에서는import {z} from 'zod'(named import)를 사용합니다. 프로젝트 전체에서 일관된 스타일을 유지하는 것이 좋습니다.참고: Zod는 두 방식 모두 지원하지만, named import가 더 일반적입니다.
♻️ named import로 통일
-import z from 'zod'; +import {z} from 'zod';🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/entities/assignment/model/schemas.ts` at line 2, Replace the default Zod import used in this module ("import z from 'zod'") with the named import style ("import { z } from 'zod'") to match the project's existing convention (as seen in the other schemas module); update any usages that reference the default identifier if necessary so they continue to use the same symbol name "z" after switching to the named import.src/entities/unit/model/useUnitStore.ts (2)
15-15: 중복 export 패턴 정리 제안
useUnitStore가 named export (Line 15)와 default export (Line 51) 모두로 내보내지고 있습니다. 일관성을 위해 하나로 통일하는 것이 좋습니다. 프로젝트에서 주로 사용하는 패턴에 맞춰주세요.♻️ named export만 유지하는 경우
export const useUnitStore = create<UnitState>()( // ... ); - -export default useUnitStore;Also applies to: 51-51
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/entities/unit/model/useUnitStore.ts` at line 15, The module currently exports useUnitStore twice (both a named export at "export const useUnitStore" and a default export later), causing duplicate export patterns; normalize to a single export style by removing the default export and keeping the named export useUnitStore (or vice versa if your project prefers default exports), update any import sites to match the chosen style, and ensure the symbol useUnitStore is the single exported identifier from this file.
24-29: 객체 프로퍼티 단축 구문 사용 제안ES6 단축 구문을 사용하면 더 간결해집니다.
♻️ 단축 구문 적용
storeFormData: (title, releaseDate, dueDate) => set({ - title: title, - releaseDate: releaseDate, - dueDate: dueDate, + title, + releaseDate, + dueDate, }),🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/entities/unit/model/useUnitStore.ts` around lines 24 - 29, storeFormData currently sets properties by repeating parameter names; update the object passed to set in the storeFormData function to use ES6 property shorthand so it becomes set({ title, releaseDate, dueDate }) instead of repeating each assignment; modify the implementation in useUnitStore where storeFormData calls set to use the shorthand while keeping the same parameter names and behavior.src/shared/model/schemas.ts (1)
6-17:semesterCodeSchema와submissionStatusSchema의 재사용성 확인두 enum 스키마가 정의되어 있지만,
src/shared/model/index.ts에서는apiResponseSchema만 re-export되고 있습니다. 다른 모듈에서 이 스키마들을 직접 import하고 있다면 문제없지만, 일관된 public API를 위해 index.ts를 통한 re-export를 고려해 보세요.♻️ index.ts에서 추가 export 제안
export type {ApiResponse, UserType} from './common'; -export {apiResponseSchema} from './schemas'; +export {apiResponseSchema, semesterCodeSchema, submissionStatusSchema} from './schemas';🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/shared/model/schemas.ts` around lines 6 - 17, Re-export the two enum schemas from the package entry so they’re available via the module index: add semesterCodeSchema and submissionStatusSchema to the exports in the shared model index (the module that currently re-exports apiResponseSchema) so other modules can import them from the public API rather than hitting the schemas file directly; update the index to export the symbols semesterCodeSchema and submissionStatusSchema (from the schemas module) and run a quick compile/CI to ensure no broken imports remain.src/entities/unit/api/unitQueries.ts (1)
23-25: 간소화 가능한 arrow function단일 표현식 반환이므로 중괄호와
return을 제거할 수 있습니다.♻️ 간소화 제안
- select: (data) => { - return data.response; - }, + select: (data) => data.response,🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/entities/unit/api/unitQueries.ts` around lines 23 - 25, Simplify the arrow function used as the select handler by converting the block body to a concise body: remove the curly braces and the explicit return in the select: (data) => { return data.response; } function so it becomes a single-expression arrow function that directly returns data.response (refer to the select handler in src/entities/unit/api/unitQueries.ts).src/pages/unit-editor/ui/UnitList.tsx (1)
14-23:handleSelectUnit함수 간소화 가능단순히
onUnitClick을 호출하는 wrapper 함수입니다. inline으로 직접 사용하거나, 함수를 제거하고onClick에서 직접 호출할 수 있습니다.♻️ 간소화 제안
export const UnitList = ({ unitList, onUnitClick, selectedUnitId, onAddNewUnit, }: UnitListProps) => { - const handleSelectUnit = (id: number) => { - onUnitClick(id); - }; - return ( <div className='flex flex-col h-full'> ... {unitList?.map(({id, title, assignmentCount}) => ( <li - onClick={() => handleSelectUnit(id)} + onClick={() => onUnitClick(id)} key={id} ...🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/pages/unit-editor/ui/UnitList.tsx` around lines 14 - 23, The handleSelectUnit wrapper in UnitList simply forwards its id to onUnitClick; remove handleSelectUnit and call onUnitClick directly from the JSX onClick handlers (or inline arrow functions) to simplify code and reduce indirection, updating any references to handleSelectUnit in UnitListProps usage to use onUnitClick instead.src/pages/manage-assignment/ui/AssignmentManageActionsBar.tsx (1)
28-33: 버튼 접근성 및 type 속성 개선 필요
- 아이콘만 있는 버튼에는 스크린 리더를 위한
aria-label추가가 권장됩니다.type="button"명시를 통해 폼 내부에서 의도치 않은 submit 방지가 가능합니다.♻️ 접근성 개선 제안
- <button onClick={handleOnEdit}> + <button type="button" onClick={handleOnEdit} aria-label="수정"> <EditIcon className='w-3.5 h-4' /> </button> - <button onClick={handleOnDelete}> + <button type="button" onClick={handleOnDelete} aria-label="삭제"> <DeleteIcon className='w-3.5 h-4' /> </button>참고: MDN - Button type attribute
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/pages/manage-assignment/ui/AssignmentManageActionsBar.tsx` around lines 28 - 33, AssignmentManageActionsBar currently renders icon-only buttons for edit/delete without type or accessible labels; update the two buttons (the ones invoking handleOnEdit and handleOnDelete and rendering EditIcon/DeleteIcon) to include type="button" to prevent accidental form submits and add descriptive aria-label attributes (e.g., aria-label="Edit assignment" and aria-label="Delete assignment") so screen readers can announce their purpose.src/entities/unit/api/unitMutations.ts (1)
8-14: 파라미터 네이밍 불일치
createUnit은unitForm을,updateUnit은unit을 사용하고 있습니다. 동일한TUnitFormSchema타입을 받으므로 일관된 네이밍을 권장합니다.♻️ 일관성 개선 제안
// 단원 수정 뮤테이션 옵션 updateUnit: { mutationKey: ['updateUnit'], - mutationFn: ({unitId, unit}: {unitId: number; unit: TUnitFormSchema}) => - updateUnit(unitId, unit), + mutationFn: ({unitId, unitForm}: {unitId: number; unitForm: TUnitFormSchema}) => + updateUnit(unitId, unitForm), },Also applies to: 20-21
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/entities/unit/api/unitMutations.ts` around lines 8 - 14, The PR has inconsistent parameter naming: createUnit expects unitForm while updateUnit uses unit though both accept TUnitFormSchema; make names consistent across the mutations by renaming the parameter in updateUnit (or in createUnit) to the same identifier (e.g., unitForm) and adjust the mutationFn signature and any usages accordingly so both createUnit and updateUnit accept a parameter named unitForm of type TUnitFormSchema; update any related references in this file (including the other occurrence around lines 20-21) to match the chosen name.src/pages/select-assignment/AssignmentSelectPage.tsx (1)
38-41: 타입 안전성 개선 제안:backPath타입 정의
location.state?.backPath가any타입으로 추론됩니다. React Router의useLocation제네릭을 활용하여 타입 안전성을 확보할 수 있습니다.♻️ 타입 정의 예시
interface LocationState { backPath?: string; } const location = useLocation() as ReturnType<typeof useLocation> & { state: LocationState | null; };🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/pages/select-assignment/AssignmentSelectPage.tsx` around lines 38 - 41, location.state?.backPath is inferred as any; make useLocation generic to type the location.state so backPath is typed (e.g., define an interface LocationState with backPath?: string and cast/use useLocation with that type). Update the location usage where returnToPreviousPage reads location.state?.backPath so it uses the typed location (refer to useLocation and returnToPreviousPage) and keep the navigate fallback (?? -1) unchanged.src/entities/course/api/courseApi.ts (1)
6-12: 인라인 스키마 정의 대신 별도 스키마 추출 고려
getAllCourses응답 스키마가 함수 내부에 인라인으로 정의되어 있습니다. 이 스키마를schemas.ts에 별도로 정의하면 재사용성과 테스트 용이성이 향상됩니다.♻️ 스키마 분리 제안
src/entities/course/model/schemas.ts에 추가:export const courseListResponseSchema = z.object({ count: z.number(), courses: z.array(dashboardCourseSchema), });그리고
courseApi.ts에서 사용:- return apiResponseSchema( - z.object({count: z.number(), courses: z.array(dashboardCourseSchema)}) - ).parse(response.data); + return apiResponseSchema(courseListResponseSchema).parse(response.data);🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/entities/course/api/courseApi.ts` around lines 6 - 12, Extract the inline response schema inside getAllCourses into a shared schema exported from src/entities/course/model/schemas.ts (e.g. export const courseListResponseSchema = z.object({count: z.number(), courses: z.array(dashboardCourseSchema)})), then import and use that schema in courseApi.ts by replacing the inline z.object with courseListResponseSchema when calling apiResponseSchema(...). Keep references to dashboardCourseSchema and apiResponseSchema unchanged.src/pages/unit-editor/UnitEditorPage.tsx (1)
39-45: 인덱스 계산 로직 개선 가능IIFE(즉시 실행 함수)를 사용한
currentIndex계산이 약간 복잡합니다.useMemo를 사용하면 의존성을 명확히 하고 불필요한 재계산을 방지할 수 있습니다.♻️ useMemo 활용 제안
const currentIndex = useMemo(() => { if (currentMode === 'creating') { return (data?.unitCount ?? 0) + 1; } const index = data?.unitList?.findIndex((u) => u.id === currentUnitId); return index !== undefined && index >= 0 ? index + 1 : 1; }, [currentMode, data?.unitCount, data?.unitList, currentUnitId]);🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/pages/unit-editor/UnitEditorPage.tsx` around lines 39 - 45, Replace the IIFE that computes currentIndex with a useMemo so the value is recomputed only when relevant dependencies change: move the logic inside a useMemo callback referencing currentMode, data?.unitCount, data?.unitList, and currentUnitId; keep the same branching (return (data?.unitCount ?? 0) + 1 when currentMode === 'creating', otherwise find index via data?.unitList.findIndex(u => u.id === currentUnitId) and return index >= 0 ? index + 1 : 1) and export the memoized result as currentIndex.src/entities/student/model/schemas.ts (1)
22-23: 날짜 필드 검증 강화 고려
startDate와endDate가z.string()으로만 검증됩니다. API 응답이 항상 유효한 날짜 형식을 보장하지 않는다면, 추가 검증을 고려해보세요.♻️ 날짜 검증 강화 예시
// ISO 날짜 형식 검증 startDate: z.string().datetime(), // 또는 커스텀 정규식 startDate: z.string().regex(/^\d{4}-\d{2}-\d{2}$/),Zod 날짜 검증 참고: Zod Strings - datetime
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/entities/student/model/schemas.ts` around lines 22 - 23, Replace the weak string-only validators for startDate and endDate in the student schema with a stronger date validation: update the startDate and endDate validators in the schema (the fields named startDate and endDate) to use z.string().datetime() for ISO datetimes or z.string().regex(/^\d{4}-\d{2}-\d{2}$/) for YYYY-MM-DD, or alternatively coerce to a Date with z.coerce.date() if you want Date objects; ensure both fields are changed consistently and add tests to cover invalid formats.src/entities/student/model/types.ts (1)
12-12: 명확한 타입명 규칙 일관성을 위해Unit을StudentUnit으로 변경 고려현재
src/entities/student/model/types.ts의Unit타입이 사용되지 않고 있어 실제 충돌은 없으나, 코드의 명확성과 일관성 측면에서 개선할 수 있습니다.다른 student 타입들(
StudentProgress,Student)이 명시적으로 접두사를 가지고 있는데 반해,Unit은src/entities/unit/model/types.ts의Unit과 동일한 이름을 사용하고 있습니다. 미래의 혼동을 방지하고 명확성을 높이기 위해StudentUnit으로 변경하는 것을 권장합니다.♻️ 타입명 변경 제안
-export type Unit = z.infer<typeof studentUnitSchema>; +export type StudentUnit = z.infer<typeof studentUnitSchema>;🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/entities/student/model/types.ts` at line 12, Rename the exported type Unit to StudentUnit to avoid collision with the unit model and to match other student-prefixed types: update the export in src/entities/student/model/types.ts from "export type Unit = z.infer<typeof studentUnitSchema>;" to "export type StudentUnit = z.infer<typeof studentUnitSchema>;", then find and update all usages/imports of Unit that refer to the student schema (e.g., places importing Unit from this module) to import and use StudentUnit instead, and adjust any tests/types that reference studentUnitSchema-derived types accordingly so symbols remain consistent.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@src/App.tsx`:
- Line 16: Update the import for AssignmentManagePage to use the project's
absolute path convention instead of a relative path: replace the current import
of AssignmentManagePage (import ... from
'./pages/manage-assignment/AssignmentManagePage') with the absolute import using
the "@/pages/manage-assignment/AssignmentManagePage" module specifier so it
matches the other page imports and coding guidelines.
In `@src/entities/auth/model/schemas.ts`:
- Around line 3-10: kakaoLoginResponseSchema currently requires studentId for
all roles causing parse errors when ADMIN responses omit it; update the schema
so role discriminates responses: use z.discriminatedUnion('role', [...]) (or
equivalent) to define two branches where ROLE === 'USER' requires studentId:
z.string(), and ROLE === 'ADMIN' allows studentId to be
z.string().nullable().optional() (or omitted), then use this updated
kakaoLoginResponseSchema in the kakaoLogin parse call so ADMIN responses no
longer throw.
In `@src/entities/unit/api/unitApi.ts`:
- Around line 18-21: getUnitById currently accepts unitId: number | null but
calls privateAxios.get(`/units/${unitId}`) without guarding against null; add a
defensive check in getUnitById to early-return or throw when unitId is
null/undefined (e.g., throw new Error or return a rejected promise) so no
request to `/units/null` is made, or change the function signature to accept
only number and rely on callers to validate — update the getUnitById function to
perform the null check before calling privateAxios.get and document the
behavior.
In `@src/features/assignment/filter-assignmnet/lib/useAssignmentList.ts`:
- Around line 12-27: The allAssignments query is always running even when a
course is selected and the truthy check treats 0 as falsy; update the useQuery
call for getAllAssignments() to run conditionally (add enabled: selectedCourseId
== null or enabled: selectedCourseId === undefined) so it only executes when no
course is selected, and change the selection check in the return logic to an
explicit null/undefined check (e.g., selectedCourseId != null) so courseId 0 is
treated as valid; keep getAssignmentsByCourse()'s enabled behavior but ensure it
uses the same explicit null/undefined condition to locate and return
course.assignments via unique().
In `@src/pages/manage-assignment/AssignmentManagePage.tsx`:
- Around line 88-89: AssignmentManagePage passes empty callbacks for the
required AssignmentPageLayout props onCancel and onConfirm; change the contract
so unused callbacks are optional by updating AssignmentPageLayout's prop types
(make onCancel?: () => void and onConfirm?: () => void) and update its render
logic to only show the confirm/cancel buttons when both callbacks are provided
(e.g., guard rendering with onCancel && onConfirm), or alternatively replace
AssignmentManagePage with a layout that doesn't expect those callbacks if you
prefer the separate-layout approach.
In `@src/shared/ui/list-row/list-row-styles.ts`:
- Line 4: The ListRow style currently lacks cursor feedback; update the
clickable state so rows show a pointer cursor: modify the ListRow rendering
(e.g., where the base style string in list-row-styles.ts and the ListRow
component consumes it) to append "cursor-pointer" when an onClick handler is
provided (or when the parent like AssignmentListContainer/AssignmentSelectPage
intends the row to be clickable), ensuring the className composition for base
(the 'base' string) or the element that renders the <li> includes
"cursor-pointer" only for interactive rows.
---
Outside diff comments:
In `@src/pages/unit-editor/ui/UnitForm.tsx`:
- Around line 66-73: The submit handler releases isSubmitting too early because
onCreateUnit/onUpdateUnit are currently synchronous (void); update
UnitEditorPage.tsx to call the TanStack Query mutateAsync (e.g., await
addUnit.mutateAsync({...}) / await updateUnit.mutateAsync(...)) and change the
exported UnitFormProps so onCreateUnit: (unit: TUnitFormSchema) => Promise<void>
and onUpdateUnit: (unitId: number, unit: TUnitFormSchema) => Promise<void>; then
make UnitForm.tsx's onSubmit async and await the calls to
onCreateUnit/onUpdateUnit (refer to onSubmit, UnitFormProps,
onCreateUnit/onUpdateUnit, and addUnit/updateUnit mutateAsync) so handleSubmit
sees the Promise and keeps isSubmitting until mutation completes.
---
Nitpick comments:
In `@src/entities/assignment/model/schemas.ts`:
- Around line 13-19: The inline object schema used inside the assignments array
should be extracted into a reusable named schema: create a const like
AssignmentItemSchema (or AssignmentSchema) defined as z.object({ course:
z.string(), section: z.string(), assignment: z.string() }) and then replace the
inline z.object(...) in assignments with z.array(AssignmentItemSchema); export
the new schema if it will be reused elsewhere and update any type aliases or
imports that should reference AssignmentItemSchema instead of the inline object.
- Line 2: Replace the default Zod import used in this module ("import z from
'zod'") with the named import style ("import { z } from 'zod'") to match the
project's existing convention (as seen in the other schemas module); update any
usages that reference the default identifier if necessary so they continue to
use the same symbol name "z" after switching to the named import.
In `@src/entities/auth/api/authApi.ts`:
- Line 3: The import of kakaoLoginResponseSchema in authApi.ts uses a relative
path; change the import to use the project's absolute alias (e.g. replace
"../model/schemas" with an absolute import like "@/entities/auth/model/schemas")
so the kakaoLoginResponseSchema import follows the `@/`... convention used across
the codebase and remains stable during refactors.
In `@src/entities/course/api/courseApi.ts`:
- Around line 6-12: Extract the inline response schema inside getAllCourses into
a shared schema exported from src/entities/course/model/schemas.ts (e.g. export
const courseListResponseSchema = z.object({count: z.number(), courses:
z.array(dashboardCourseSchema)})), then import and use that schema in
courseApi.ts by replacing the inline z.object with courseListResponseSchema when
calling apiResponseSchema(...). Keep references to dashboardCourseSchema and
apiResponseSchema unchanged.
In `@src/entities/student/model/schemas.ts`:
- Around line 22-23: Replace the weak string-only validators for startDate and
endDate in the student schema with a stronger date validation: update the
startDate and endDate validators in the schema (the fields named startDate and
endDate) to use z.string().datetime() for ISO datetimes or
z.string().regex(/^\d{4}-\d{2}-\d{2}$/) for YYYY-MM-DD, or alternatively coerce
to a Date with z.coerce.date() if you want Date objects; ensure both fields are
changed consistently and add tests to cover invalid formats.
In `@src/entities/student/model/types.ts`:
- Line 12: Rename the exported type Unit to StudentUnit to avoid collision with
the unit model and to match other student-prefixed types: update the export in
src/entities/student/model/types.ts from "export type Unit = z.infer<typeof
studentUnitSchema>;" to "export type StudentUnit = z.infer<typeof
studentUnitSchema>;", then find and update all usages/imports of Unit that refer
to the student schema (e.g., places importing Unit from this module) to import
and use StudentUnit instead, and adjust any tests/types that reference
studentUnitSchema-derived types accordingly so symbols remain consistent.
In `@src/entities/unit/api/unitMutations.ts`:
- Around line 8-14: The PR has inconsistent parameter naming: createUnit expects
unitForm while updateUnit uses unit though both accept TUnitFormSchema; make
names consistent across the mutations by renaming the parameter in updateUnit
(or in createUnit) to the same identifier (e.g., unitForm) and adjust the
mutationFn signature and any usages accordingly so both createUnit and
updateUnit accept a parameter named unitForm of type TUnitFormSchema; update any
related references in this file (including the other occurrence around lines
20-21) to match the chosen name.
In `@src/entities/unit/api/unitQueries.ts`:
- Around line 23-25: Simplify the arrow function used as the select handler by
converting the block body to a concise body: remove the curly braces and the
explicit return in the select: (data) => { return data.response; } function so
it becomes a single-expression arrow function that directly returns
data.response (refer to the select handler in
src/entities/unit/api/unitQueries.ts).
In `@src/entities/unit/model/useUnitStore.ts`:
- Line 15: The module currently exports useUnitStore twice (both a named export
at "export const useUnitStore" and a default export later), causing duplicate
export patterns; normalize to a single export style by removing the default
export and keeping the named export useUnitStore (or vice versa if your project
prefers default exports), update any import sites to match the chosen style, and
ensure the symbol useUnitStore is the single exported identifier from this file.
- Around line 24-29: storeFormData currently sets properties by repeating
parameter names; update the object passed to set in the storeFormData function
to use ES6 property shorthand so it becomes set({ title, releaseDate, dueDate })
instead of repeating each assignment; modify the implementation in useUnitStore
where storeFormData calls set to use the shorthand while keeping the same
parameter names and behavior.
In `@src/pages/manage-assignment/ui/AssignmentManageActionsBar.tsx`:
- Around line 28-33: AssignmentManageActionsBar currently renders icon-only
buttons for edit/delete without type or accessible labels; update the two
buttons (the ones invoking handleOnEdit and handleOnDelete and rendering
EditIcon/DeleteIcon) to include type="button" to prevent accidental form submits
and add descriptive aria-label attributes (e.g., aria-label="Edit assignment"
and aria-label="Delete assignment") so screen readers can announce their
purpose.
In `@src/pages/select-assignment/AssignmentSelectPage.tsx`:
- Around line 38-41: location.state?.backPath is inferred as any; make
useLocation generic to type the location.state so backPath is typed (e.g.,
define an interface LocationState with backPath?: string and cast/use
useLocation with that type). Update the location usage where
returnToPreviousPage reads location.state?.backPath so it uses the typed
location (refer to useLocation and returnToPreviousPage) and keep the navigate
fallback (?? -1) unchanged.
In `@src/pages/unit-editor/model/types.ts`:
- Around line 10-13: UnitFormProps currently allows any combination of mode,
unitIndex and handlers which weakens type safety; change it to a discriminated
union keyed on mode (the Mode type) by creating separate interfaces (e.g.,
UnitFormCreateProps with mode: 'create' and required onCreateUnit only;
UnitFormEditProps with mode: 'edit', unitIndex: number and required
onUpdateUnit; UnitFormDeleteProps with mode: 'delete', unitIndex: number and
required onDeleteUnit) and export type UnitFormProps = UnitFormCreateProps |
UnitFormEditProps | UnitFormDeleteProps; ensure you reference the exact symbols
TUnitFormSchema, Mode, unitIndex, onCreateUnit, onUpdateUnit and onDeleteUnit
when renaming/organizing so callers and components are updated to match the new
discriminated union shape.
In `@src/pages/unit-editor/ui/UnitList.tsx`:
- Around line 14-23: The handleSelectUnit wrapper in UnitList simply forwards
its id to onUnitClick; remove handleSelectUnit and call onUnitClick directly
from the JSX onClick handlers (or inline arrow functions) to simplify code and
reduce indirection, updating any references to handleSelectUnit in UnitListProps
usage to use onUnitClick instead.
In `@src/pages/unit-editor/UnitEditorPage.tsx`:
- Around line 39-45: Replace the IIFE that computes currentIndex with a useMemo
so the value is recomputed only when relevant dependencies change: move the
logic inside a useMemo callback referencing currentMode, data?.unitCount,
data?.unitList, and currentUnitId; keep the same branching (return
(data?.unitCount ?? 0) + 1 when currentMode === 'creating', otherwise find index
via data?.unitList.findIndex(u => u.id === currentUnitId) and return index >= 0
? index + 1 : 1) and export the memoized result as currentIndex.
In `@src/shared/model/schemas.ts`:
- Around line 6-17: Re-export the two enum schemas from the package entry so
they’re available via the module index: add semesterCodeSchema and
submissionStatusSchema to the exports in the shared model index (the module that
currently re-exports apiResponseSchema) so other modules can import them from
the public API rather than hitting the schemas file directly; update the index
to export the symbols semesterCodeSchema and submissionStatusSchema (from the
schemas module) and run a quick compile/CI to ensure no broken imports remain.
ℹ️ Review info
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (39)
src/App.tsxsrc/entities/assignment/api/assignmentApi.tssrc/entities/assignment/api/assignmentMutations.tssrc/entities/assignment/api/assignmentQueries.tssrc/entities/assignment/api/assignmentQueryOptions.tssrc/entities/assignment/model/schemas.tssrc/entities/assignment/model/types.tssrc/entities/auth/api/authApi.tssrc/entities/auth/model/schemas.tssrc/entities/course/api/courseApi.tssrc/entities/course/api/courseMutations.tssrc/entities/course/api/courseQueries.tssrc/entities/course/api/courseQueryOptions.tssrc/entities/course/index.tssrc/entities/course/model/schemas.tssrc/entities/course/model/types.tssrc/entities/student/model/schemas.tssrc/entities/student/model/types.tssrc/entities/unit/api/unitApi.tssrc/entities/unit/api/unitMutations.tssrc/entities/unit/api/unitQueries.tssrc/entities/unit/model/types.tssrc/entities/unit/model/useUnitStore.tssrc/features/assignment/filter-assignmnet/lib/useAssignmentList.tssrc/pages/dashboard/Dashboard.tsxsrc/pages/manage-assignment/AssignmentManagePage.tsxsrc/pages/manage-assignment/ui/AssignmentManageActionsBar.tsxsrc/pages/select-assignment/AssignmentSelectPage.tsxsrc/pages/select-assignment/ui/AssignmentListContainer.tsxsrc/pages/unit-editor/UnitEditorPage.tsxsrc/pages/unit-editor/model/types.tssrc/pages/unit-editor/ui/UnitForm.tsxsrc/pages/unit-editor/ui/UnitList.tsxsrc/shared/model/index.tssrc/shared/model/schemas.tssrc/shared/ui/Layout.tsxsrc/shared/ui/button/Button.tsxsrc/shared/ui/list-row/ListRow.tsxsrc/shared/ui/list-row/list-row-styles.ts
💤 Files with no reviewable changes (3)
- src/entities/course/api/courseQueryOptions.ts
- src/entities/course/index.ts
- src/entities/assignment/api/assignmentQueryOptions.ts
| export const kakaoLoginResponseSchema = z.object({ | ||
| memberId: z.number(), | ||
| name: z.string(), | ||
| role: z.enum(['ADMIN', 'USER']), | ||
| studentId: z.string(), | ||
| email: z.string().nullable(), | ||
| accessToken: z.string(), | ||
| }); |
There was a problem hiding this comment.
studentId 스키마가 역할별 응답 차이를 막아 로그인 실패를 유발할 수 있어요
Line 7의 studentId: z.string()는 항상 필수 문자열을 강제합니다. 그런데 현재 호출부(kakaoLogin)는 role이 ADMIN | USER이고 studentId는 optional이라, ADMIN 응답에서 studentId가 없거나 null이면 parse가 즉시 throw 되어 로그인 플로우가 깨질 수 있습니다.
개선 제안 (역할별 응답을 명시적으로 검증)
export const kakaoLoginResponseSchema = z.object({
memberId: z.number(),
name: z.string(),
- role: z.enum(['ADMIN', 'USER']),
- studentId: z.string(),
+ role: z.enum(['ADMIN', 'USER']),
+ studentId: z.string().nullable().optional(),
email: z.string().nullable(),
accessToken: z.string(),
});필요하면 더 엄격하게 z.discriminatedUnion('role', ...)으로 USER일 때만 studentId 필수로 강제하는 방식이 가장 안전합니다.
공식 문서: https://zod.dev/?id=optionals , https://zod.dev/?id=discriminated-unions
📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| export const kakaoLoginResponseSchema = z.object({ | |
| memberId: z.number(), | |
| name: z.string(), | |
| role: z.enum(['ADMIN', 'USER']), | |
| studentId: z.string(), | |
| email: z.string().nullable(), | |
| accessToken: z.string(), | |
| }); | |
| export const kakaoLoginResponseSchema = z.object({ | |
| memberId: z.number(), | |
| name: z.string(), | |
| role: z.enum(['ADMIN', 'USER']), | |
| studentId: z.string().nullable().optional(), | |
| email: z.string().nullable(), | |
| accessToken: z.string(), | |
| }); |
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@src/entities/auth/model/schemas.ts` around lines 3 - 10,
kakaoLoginResponseSchema currently requires studentId for all roles causing
parse errors when ADMIN responses omit it; update the schema so role
discriminates responses: use z.discriminatedUnion('role', [...]) (or equivalent)
to define two branches where ROLE === 'USER' requires studentId: z.string(), and
ROLE === 'ADMIN' allows studentId to be z.string().nullable().optional() (or
omitted), then use this updated kakaoLoginResponseSchema in the kakaoLogin parse
call so ADMIN responses no longer throw.
| const {data: allAssignments} = useQuery( | ||
| assignmentQueries.getAllAssignments() | ||
| ); | ||
| const {data: assignments} = useQuery( | ||
| assignmentQueries.getAssignmentsByCourse(selectedCourseId ?? 0) | ||
| ); | ||
|
|
||
| if (selectedCourseId) { | ||
| return unique( | ||
| assignments?.response.courses.flatMap((course) => | ||
| course.id === selectedCourseId ? course.assignments : [] | ||
| ) ?? [] | ||
| ); | ||
| } | ||
|
|
||
| return unique(allAssignments?.response.assignments ?? []); |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
sed -n '1,50p' src/features/assignment/filter-assignmnet/lib/useAssignmentList.tsRepository: 2025-snowCode/snowCode-Client
Length of output: 926
🏁 Script executed:
find . -name "assignmentQueries*" -type fRepository: 2025-snowCode/snowCode-Client
Length of output: 123
🏁 Script executed:
rg -t ts "assignmentQueries\.(getAllAssignments|getAssignmentsByCourse)" -A 3 -B 1Repository: 2025-snowCode/snowCode-Client
Length of output: 1539
🏁 Script executed:
cat -n src/entities/assignment/api/assignmentQueries.tsRepository: 2025-snowCode/snowCode-Client
Length of output: 1036
🏁 Script executed:
grep -r "mock\|Mock" src/entities/assignment/api/ -A 2 -B 2Repository: 2025-snowCode/snowCode-Client
Length of output: 55
선택 강의가 있을 때 전체 과제 쿼리가 불필요하게 실행됩니다.
getAssignmentsByCourse()는 이미 enabled: !!courseId 조건이 있지만, getAllAssignments()는 selectedCourseId 값과 관계없이 항상 실행됩니다. 또한 Line 19의 truthy 체크는 0도 유효한 강의 ID일 수 있으므로 명시적인 null 체크가 필요합니다.
🔧 제안 수정안
const {data: allAssignments} = useQuery(
- assignmentQueries.getAllAssignments()
+ {
+ ...assignmentQueries.getAllAssignments(),
+ enabled: selectedCourseId === null,
+ }
);
- if (selectedCourseId) {
+ if (selectedCourseId !== null) {📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| const {data: allAssignments} = useQuery( | |
| assignmentQueries.getAllAssignments() | |
| ); | |
| const {data: assignments} = useQuery( | |
| assignmentQueries.getAssignmentsByCourse(selectedCourseId ?? 0) | |
| ); | |
| if (selectedCourseId) { | |
| return unique( | |
| assignments?.response.courses.flatMap((course) => | |
| course.id === selectedCourseId ? course.assignments : [] | |
| ) ?? [] | |
| ); | |
| } | |
| return unique(allAssignments?.response.assignments ?? []); | |
| const {data: allAssignments} = useQuery( | |
| { | |
| ...assignmentQueries.getAllAssignments(), | |
| enabled: selectedCourseId === null, | |
| } | |
| ); | |
| const {data: assignments} = useQuery( | |
| assignmentQueries.getAssignmentsByCourse(selectedCourseId ?? 0) | |
| ); | |
| if (selectedCourseId !== null) { | |
| return unique( | |
| assignments?.response.courses.flatMap((course) => | |
| course.id === selectedCourseId ? course.assignments : [] | |
| ) ?? [] | |
| ); | |
| } | |
| return unique(allAssignments?.response.assignments ?? []); |
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@src/features/assignment/filter-assignmnet/lib/useAssignmentList.ts` around
lines 12 - 27, The allAssignments query is always running even when a course is
selected and the truthy check treats 0 as falsy; update the useQuery call for
getAllAssignments() to run conditionally (add enabled: selectedCourseId == null
or enabled: selectedCourseId === undefined) so it only executes when no course
is selected, and change the selection check in the return logic to an explicit
null/undefined check (e.g., selectedCourseId != null) so courseId 0 is treated
as valid; keep getAssignmentsByCourse()'s enabled behavior but ensure it uses
the same explicit null/undefined condition to locate and return
course.assignments via unique().
| onCancel={() => {}} | ||
| onConfirm={() => {}} |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
# Find the AssignmentManagePage.tsx file and examine lines 88-89
if git ls-files src/pages/manage-assignment/AssignmentManagePage.tsx; then
echo "=== File found, examining lines 80-95 ==="
sed -n '80,95p' src/pages/manage-assignment/AssignmentManagePage.tsx
echo ""
echo "=== Total line count ==="
wc -l src/pages/manage-assignment/AssignmentManagePage.tsx
fiRepository: 2025-snowCode/snowCode-Client
Length of output: 547
🏁 Script executed:
#!/bin/bash
# Search for AssignmentPageLayout definition
echo "=== Searching for AssignmentPageLayout component ==="
fd -t f -e tsx -e ts | xargs rg -l "AssignmentPageLayout" | head -20Repository: 2025-snowCode/snowCode-Client
Length of output: 338
🏁 Script executed:
#!/bin/bash
# Look for AssignmentPageLayout in widgets directory
echo "=== AssignmentPageLayout files ==="
fd -t f "AssignmentPageLayout" src/Repository: 2025-snowCode/snowCode-Client
Length of output: 170
🏁 Script executed:
#!/bin/bash
# Examine AssignmentPageLayout component definition
echo "=== AssignmentPageLayout component ==="
cat src/widgets/assignment-page-layout/ui/AssignmentPageLayout.tsxRepository: 2025-snowCode/snowCode-Client
Length of output: 1381
🏁 Script executed:
#!/bin/bash
# Check how AssignmentPageLayout is used in AssignmentSelectPage
echo "=== AssignmentSelectPage usage ==="
sed -n '1,150p' src/pages/select-assignment/AssignmentSelectPage.tsx | tail -50Repository: 2025-snowCode/snowCode-Client
Length of output: 1369
🏁 Script executed:
#!/bin/bash
# Check full context of AssignmentManagePage.tsx
echo "=== Full AssignmentManagePage.tsx ==="
cat -n src/pages/manage-assignment/AssignmentManagePage.tsxRepository: 2025-snowCode/snowCode-Client
Length of output: 3822
AssignmentPageLayout의 필수 props와 실제 사용 패턴의 불일치 해결 필요
AssignmentPageLayout 컴포넌트의 props 정의를 확인한 결과, onCancel과 onConfirm이 필수 props입니다. 그러나 현재 페이지에서는 두 콜백 모두 빈 함수로 전달되고 있습니다.
현황 분석:
AssignmentSelectPage는 사용자가 문제를 선택하고 확인/취소하는 명확한 워크플로우가 있어 이 props가 의미 있습니다.AssignmentManagePage는 문제를 조회하고 삭제하는 페이지로, 확인/취소 버튼의 용도가 불명확합니다. 실제로 사용자의 의도를 반영하는 로직이 없습니다.
개선 방안:
-
권장:
AssignmentPageLayout에서onCancel과onConfirm을 선택적 props으로 변경하고, 이 props가 없을 때는 버튼을 렌더링하지 않도록 수정하세요.interface AssignmentPageLayoutProps { // ... onCancel?: () => void; onConfirm?: () => void; } // 렌더링 시: {onCancel && onConfirm && <div className='...'>...</div>}
-
대체 방안: 페이지의 실제 필요에 맞는 별도의 레이아웃 컴포넌트 사용을 검토하세요.
현재 형태는 사용하지 않는 UI 요소가 사용자에게 노출될 수 있어 좋은 사용성 경험을 제공하지 못합니다. React Props 패턴 가이드를 참고하시면 도움이 될 것 같습니다.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@src/pages/manage-assignment/AssignmentManagePage.tsx` around lines 88 - 89,
AssignmentManagePage passes empty callbacks for the required
AssignmentPageLayout props onCancel and onConfirm; change the contract so unused
callbacks are optional by updating AssignmentPageLayout's prop types (make
onCancel?: () => void and onConfirm?: () => void) and update its render logic to
only show the confirm/cancel buttons when both callbacks are provided (e.g.,
guard rendering with onCancel && onConfirm), or alternatively replace
AssignmentManagePage with a layout that doesn't expect those callbacks if you
prefer the separate-layout approach.
|
|
||
| export const ListRowStyles = tv({ | ||
| base: 'cursor-pointer bg-background w-full flex items-center rounded-[9px] pl-4.5 pr-7.5 py-4 gap-4 border', | ||
| base: 'bg-background w-full flex items-center rounded-[9px] pl-4.5 pr-7.5 py-4 gap-4 border', |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
# Find ListRow usage patterns - searching for clickable instances
rg "ListRow" --type=tsx --type=ts -B 2 -A 3Repository: 2025-snowCode/snowCode-Client
Length of output: 100
🏁 Script executed:
# Check the ListRow component definition
find . -name "*list-row*" -type f | head -20Repository: 2025-snowCode/snowCode-Client
Length of output: 116
🏁 Script executed:
# Look for the ListRow component implementation
cat src/shared/ui/list-row/list-row.tsx 2>/dev/null || find . -path "*/list-row/*" -name "*.tsx" -o -name "*.ts"Repository: 2025-snowCode/snowCode-Client
Length of output: 50386
🏁 Script executed:
cat src/shared/ui/list-row/ListRow.tsxRepository: 2025-snowCode/snowCode-Client
Length of output: 700
🏁 Script executed:
# Search for ListRow usage with correct syntax
rg "ListRow" --include="*.tsx" --include="*.ts" -B 2 -A 3 | head -100Repository: 2025-snowCode/snowCode-Client
Length of output: 514
🏁 Script executed:
cat src/shared/ui/list-row/list-row-styles.tsRepository: 2025-snowCode/snowCode-Client
Length of output: 485
🏁 Script executed:
# Search for ListRow usage with correct syntax
rg "ListRow" -A 3 | head -150Repository: 2025-snowCode/snowCode-Client
Length of output: 4072
🏁 Script executed:
rg "ListRow" -A 10 | grep -E "ListRow|onClick" | head -50Repository: 2025-snowCode/snowCode-Client
Length of output: 2387
🏁 Script executed:
# Check the AssignmentSelectPage to see if ListRow is clickable there
cat src/pages/select-assignment/AssignmentSelectPage.tsx | head -100Repository: 2025-snowCode/snowCode-Client
Length of output: 2555
🏁 Script executed:
# Check AssignmentListContainer to see how ListRow is wrapped
rg "AssignmentListContainer" -A 20 | head -80Repository: 2025-snowCode/snowCode-Client
Length of output: 7506
🏁 Script executed:
# Look for the actual AssignmentListContainer file
find . -name "*AssignmentListContainer*" -type fRepository: 2025-snowCode/snowCode-Client
Length of output: 133
🏁 Script executed:
cat src/pages/select-assignment/ui/AssignmentListContainer.tsxRepository: 2025-snowCode/snowCode-Client
Length of output: 1151
🏁 Script executed:
# Verify if cursor-pointer is applied to the <li> element in AssignmentListContainer or elsewhere
rg "cursor-pointer" -n src/pages/select-assignment/Repository: 2025-snowCode/snowCode-Client
Length of output: 55
ListRow 클릭 가능 시 커서 피드백 추가 필요
기본 스타일에서 cursor-pointer가 제거되었는데, AssignmentSelectPage 같은 페이지에서 ListRow가 클릭 가능한 요소로 사용될 때는 사용자가 상호작용 가능 여부를 인지하기 어렵습니다.
AssignmentListContainer의 <li> 요소에 cursor-pointer를 추가하거나, ListRow에 onClick 핸들러를 전달할 때 스타일을 함께 적용하는 방식으로 개선해주세요. 이렇게 하면 클릭 가능한 행과 그렇지 않은 행을 명확히 구분할 수 있습니다.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@src/shared/ui/list-row/list-row-styles.ts` at line 4, The ListRow style
currently lacks cursor feedback; update the clickable state so rows show a
pointer cursor: modify the ListRow rendering (e.g., where the base style string
in list-row-styles.ts and the ListRow component consumes it) to append
"cursor-pointer" when an onClick handler is provided (or when the parent like
AssignmentListContainer/AssignmentSelectPage intends the row to be clickable),
ensuring the className composition for base (the 'base' string) or the element
that renders the <li> includes "cursor-pointer" only for interactive rows.
There was a problem hiding this comment.
Actionable comments posted: 2
🧹 Nitpick comments (1)
src/entities/auth/api/authApi.ts (1)
16-16: Zod 파싱 실패 시 에러 처리가 필요합니다.
kakaoLoginResponseSchema.parse()는 유효성 검증 실패 시ZodError를 throw합니다. 현재 코드에서는 이 예외를 처리하지 않아, API 응답이 예상과 다를 경우 호출부에서 예기치 않은 에러가 발생할 수 있습니다.두 가지 접근 방법을 고려해 보세요:
- safeParse 사용: 에러를 throw하지 않고 결과 객체로 반환
- try-catch로 감싸기: ZodError를 의미 있는 에러로 변환
🛡️ safeParse를 활용한 개선 예시
- const data = kakaoLoginResponseSchema.parse(response.data.response); + const result = kakaoLoginResponseSchema.safeParse(response.data.response); + if (!result.success) { + console.error('API 응답 검증 실패:', result.error.flatten()); + throw new Error('로그인 응답 형식이 올바르지 않습니다.'); + } + const data = result.data;📚 참고: Zod 공식 문서 - safeParse
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/entities/auth/api/authApi.ts` at line 16, kakaoLoginResponseSchema.parse(response.data.response)에서 ZodError가 throw될 수 있으니 예외 처리를 추가하세요: 대신 kakaoLoginResponseSchema.safeParse(response.data.response)를 사용해 결과.ok를 체크하고 실패 시 적절한 에러 객체(예: ApiError 또는 명확한 메시지와 함께 status code)를 반환하거나, 기존 호출 흐름에 맞게 실패를 throw 하도록 구현하거나, 대안으로 parse 호출을 try-catch로 감싸고 catch에서 ZodError를 잡아 의미 있는 오류로 변환해서 던지거나 로깅하도록 변경하세요; 참조 대상 심볼: kakaoLoginResponseSchema.parse, response.data.response, kakaoLoginResponseSchema.safeParse.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@src/entities/student/model/types.ts`:
- Around line 3-8: The import uses `import type` for runtime Zod schemas
(progressStatusSchema, studentProgressSchema, studentUnitSchema, studentSchema)
which causes "Cannot use as a value because it was imported using 'import
type'"; change the import to a regular runtime import (remove `type`) so the Zod
schema symbols are available at runtime for `z.infer<typeof ...>` usage in this
file (types.ts) and keep existing `z.infer` usages unchanged.
In `@src/pages/course-overview/ui/CourseHero.tsx`:
- Line 70: In the CourseHero component update the invalid Tailwind classes
`pb-2.75` and `pb-12.5` (these don't exist in the project's Tailwind v4 spacing
scale) by either: 1) switching to a valid scale like `pb-2.5` / `pb-3` and
`pb-12` respectively, 2) using Tailwind arbitrary values such as `pb-[11px]` or
`pb-[2.75rem]`, or 3) adding custom spacing tokens to your Tailwind theme (in
the `@theme` spacing block) and then using those new keys; locate the class
names in the CourseHero JSX and apply one of these fixes consistently for both
occurrences.
---
Nitpick comments:
In `@src/entities/auth/api/authApi.ts`:
- Line 16: kakaoLoginResponseSchema.parse(response.data.response)에서 ZodError가
throw될 수 있으니 예외 처리를 추가하세요: 대신
kakaoLoginResponseSchema.safeParse(response.data.response)를 사용해 결과.ok를 체크하고 실패 시
적절한 에러 객체(예: ApiError 또는 명확한 메시지와 함께 status code)를 반환하거나, 기존 호출 흐름에 맞게 실패를 throw
하도록 구현하거나, 대안으로 parse 호출을 try-catch로 감싸고 catch에서 ZodError를 잡아 의미 있는 오류로 변환해서
던지거나 로깅하도록 변경하세요; 참조 대상 심볼: kakaoLoginResponseSchema.parse,
response.data.response, kakaoLoginResponseSchema.safeParse.
ℹ️ Review info
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (8)
src/entities/auth/api/authApi.tssrc/entities/auth/model/useUserStore.tssrc/entities/student/model/types.tssrc/pages/course-overview/ui/CourseHero.tsxsrc/shared/lib/course.tssrc/shared/model/index.tssrc/shared/model/schemas.tssrc/shared/model/type.ts
💤 Files with no reviewable changes (1)
- src/shared/model/type.ts
🚧 Files skipped from review as they are similar to previous changes (1)
- src/shared/model/schemas.ts
| <h1 className='pb-[1px] text-2xl font-medium leading-9'>{title}</h1> | ||
| <p className='pb-[11px] text-base font-normal leading-6'> | ||
| <h1 className='pb-px text-2xl font-medium leading-9'>{title}</h1> | ||
| <p className='pb-2.75 text-base font-normal leading-6'> |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
# Find tailwind config files
echo "=== Searching for tailwind config files ==="
fd -e js -e ts -e mjs -e cjs 'tailwind\.config' --exec cat {} 2>/dev/null
# Also search for tailwind configuration in package.json or other config formats
echo ""
echo "=== Checking for tailwind config in other formats ==="
find . -maxdepth 2 -name "tailwind.config.*" -type f 2>/dev/null | head -5Repository: 2025-snowCode/snowCode-Client
Length of output: 171
🏁 Script executed:
#!/bin/bash
# Let's also check the actual file mentioned in the review
echo "=== Checking CourseHero.tsx file around line 70 ==="
if [ -f "src/pages/course-overview/ui/CourseHero.tsx" ]; then
sed -n '65,75p' src/pages/course-overview/ui/CourseHero.tsx
else
echo "File not found at expected path, searching for CourseHero.tsx"
fd "CourseHero.tsx" --exec cat {} \; 2>/dev/null | head -100
fiRepository: 2025-snowCode/snowCode-Client
Length of output: 550
🏁 Script executed:
#!/bin/bash
# Search for pb-2.75 usage in the codebase
echo "=== Searching for pb-2.75 usage ==="
rg "pb-2\.75" --type tsx --type tsRepository: 2025-snowCode/snowCode-Client
Length of output: 153
🏁 Script executed:
#!/bin/bash
# Search for all config files at root
echo "=== Files at root level ==="
ls -la *.config.* 2>/dev/null || echo "No config files at root"
# Search for tailwind config in all locations
echo ""
echo "=== Searching for tailwind config anywhere ==="
find . -name "*tailwind*" -type f 2>/dev/null | grep -v node_modules | head -20
# Check if there's a tsconfig or jsconfig with paths
echo ""
echo "=== Checking tsconfig.json for custom paths ==="
if [ -f "tsconfig.json" ]; then
cat tsconfig.json | head -30
fiRepository: 2025-snowCode/snowCode-Client
Length of output: 514
🏁 Script executed:
#!/bin/bash
# Search for pb-2.75 in all files
echo "=== Searching for pb-2.75 in codebase ==="
rg "pb-2\.75" --type-list | head -20
rg "pb-2\.75" -t js -t ts --no-heading 2>/dev/null || rg "pb-2\.75" --no-heading || grep -r "pb-2\.75" . --include="*.tsx" --include="*.ts" --include="*.jsx" --include="*.js" 2>/dev/null | head -10
# Also check for custom spacing configuration
echo ""
echo "=== Searching for custom spacing in config files ==="
grep -r "pb-2\.75\|2\.75" . --include="*.json" --include="*.js" --include="*.ts" 2>/dev/null | grep -v node_modules | head -10Repository: 2025-snowCode/snowCode-Client
Length of output: 681
🏁 Script executed:
#!/bin/bash
# Check package.json for Tailwind version and config
echo "=== Checking package.json ==="
if [ -f "package.json" ]; then
cat package.json | grep -A 5 -B 5 "tailwind" || cat package.json | head -50
fi
# Search for any CSS configuration files
echo ""
echo "=== Checking for CSS and style configuration ==="
find . -maxdepth 3 -name "*.css" -type f 2>/dev/null | grep -v node_modules | head -10Repository: 2025-snowCode/snowCode-Client
Length of output: 1114
🏁 Script executed:
#!/bin/bash
# Check the Tailwind CSS setup file
echo "=== Checking src/index.css ==="
cat src/index.css
echo ""
echo "=== Checking if there are any global CSS files with custom spacing ==="
find src -name "*.css" -type f 2>/dev/null | xargs cat 2>/dev/null | head -50Repository: 2025-snowCode/snowCode-Client
Length of output: 3804
🏁 Script executed:
#!/bin/bash
# Check vite.config.ts for tailwind setup
echo "=== Checking vite.config.ts ==="
cat vite.config.ts
echo ""
echo "=== Check if pb-2.75 could be an arbitrary value in Tailwind v4 ==="
# In Tailwind v4, arbitrary values use square bracket notation like pb-[2.75rem]
# Let's verify the exact class being used
echo "Exact class in CourseHero.tsx:"
grep -o "pb-[^ ]*" src/pages/course-overview/ui/CourseHero.tsx | sort | uniqRepository: 2025-snowCode/snowCode-Client
Length of output: 1004
pb-2.75는 Tailwind 기본 스페이싱 스케일에 없는 값입니다. 설정 필요
현재 프로젝트의 Tailwind CSS v4 설정에서 pb-2.75는 정의되지 않았습니다. 기본 스페이싱 스케일(2, 2.5, 3, 3.5...)에 포함되지 않는 값이므로 스타일이 적용되지 않습니다.
아래 중 하나를 선택하세요:
src/index.css의@theme블록에 커스텀 스페이싱 추가- 임의 값 사용:
pb-[2.75rem](또는 픽셀 단위로pb-[11px]) - 기본 스케일 값으로 변경:
pb-2.5또는pb-3
같은 파일의 pb-12.5도 동일한 문제가 있으니 함께 수정하세요.
📚 참고: Tailwind CSS v4 Theme Configuration
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@src/pages/course-overview/ui/CourseHero.tsx` at line 70, In the CourseHero
component update the invalid Tailwind classes `pb-2.75` and `pb-12.5` (these
don't exist in the project's Tailwind v4 spacing scale) by either: 1) switching
to a valid scale like `pb-2.5` / `pb-3` and `pb-12` respectively, 2) using
Tailwind arbitrary values such as `pb-[11px]` or `pb-[2.75rem]`, or 3) adding
custom spacing tokens to your Tailwind theme (in the `@theme` spacing block) and
then using those new keys; locate the class names in the CourseHero JSX and
apply one of these fixes consistently for both occurrences.
There was a problem hiding this comment.
🧹 Nitpick comments (1)
src/entities/student/model/types.ts (1)
3-8: 절대 경로 import로 통일해 주세요Line 3-8은 상대 경로(
./schemas)라서 프로젝트 규칙과 어긋납니다. alias 경로(@/...)로 맞추면 모듈 이동/리팩터링 시 안정성이 좋아집니다.
참고: https://nextjs.org/docs/app/building-your-application/configuring/absolute-imports-and-module-aliases변경 제안
import { progressStatusSchema, studentProgressSchema, studentUnitSchema, studentSchema, -} from './schemas'; +} from '@/entities/student/model/schemas';As per coding guidelines
src/**: "절대 경로 임포트(@/...) 사용".🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/entities/student/model/types.ts` around lines 3 - 8, The import in types.ts currently pulls progressStatusSchema, studentProgressSchema, studentUnitSchema, and studentSchema via a relative path ('./schemas'); update that import to use the project alias/absolute path (e.g., '@/...') so it follows the src/** coding guideline and remains stable during refactors—locate the import that references './schemas' and replace it with the corresponding alias import for the same exported symbols.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Nitpick comments:
In `@src/entities/student/model/types.ts`:
- Around line 3-8: The import in types.ts currently pulls progressStatusSchema,
studentProgressSchema, studentUnitSchema, and studentSchema via a relative path
('./schemas'); update that import to use the project alias/absolute path (e.g.,
'@/...') so it follows the src/** coding guideline and remains stable during
refactors—locate the import that references './schemas' and replace it with the
corresponding alias import for the same exported symbols.
ℹ️ Review info
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (4)
src/App.tsxsrc/entities/student/model/types.tssrc/entities/unit/api/unitApi.tssrc/entities/unit/api/unitQueries.ts
🚧 Files skipped from review as they are similar to previous changes (3)
- src/App.tsx
- src/entities/unit/api/unitQueries.ts
- src/entities/unit/api/unitApi.ts
There was a problem hiding this comment.
Actionable comments posted: 1
🧹 Nitpick comments (2)
src/entities/assignment/api/assignmentApi.ts (1)
4-4: 절대 경로 임포트로 통일해 주세요.Line 4는 상대 경로(
../model/schemas)를 사용하고 있어 이 파일의 나머지 임포트 스타일과도 불일치합니다. alias 경로(@/...)로 맞추는 편이 리팩터링 내성이 좋습니다.변경 제안
-import {assignmentScheduleSchema} from '../model/schemas'; +import {assignmentScheduleSchema} from '@/entities/assignment/model/schemas';As per coding guidelines
src/**: "절대 경로 임포트(@/...) 사용".🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/entities/assignment/api/assignmentApi.ts` at line 4, Replace the relative import of assignmentScheduleSchema with the project's absolute alias import form; locate the import statement that references assignmentScheduleSchema and change it from a "../" relative path to the alias-based path used across the codebase (use the "@/..." alias to point to the model/schemas module) so imports are consistent and refactor-safe.src/entities/unit/model/schemas.ts (1)
23-25: 날짜 문자열 비교 방식 확인 필요
releaseDate <= dueDate비교는 ISO 8601 형식(YYYY-MM-DD)일 때만 올바르게 동작합니다. 입력 형식이 다르거나 시간이 포함된 경우 예상치 못한 결과가 발생할 수 있습니다.현재
type='date'입력을 사용하고 있어 ISO 형식이 보장되지만, 방어적 코딩을 위해Date객체로 변환 후 비교하는 것도 고려해볼 수 있습니다:💡 Date 객체 비교 방식 제안
.refine((data) => data.releaseDate <= data.dueDate, { + // 또는 더 안전한 Date 비교: + // .refine((data) => new Date(data.releaseDate) <= new Date(data.dueDate), { message: '날짜 범위가 올바르지 않습니다.', path: ['dueDate'], });🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/entities/unit/model/schemas.ts` around lines 23 - 25, The refine check in schemas.ts currently compares releaseDate <= dueDate as strings; change the .refine on the schema that references releaseDate and dueDate to parse both into Date objects (e.g., via new Date(...) or Date.parse) and compare their numeric time values (dateA.getTime() <= dateB.getTime()), also ensure you guard against invalid dates (isNaN) and return false (with the same message/path) when parsing fails so invalid inputs are rejected.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@src/pages/unit-editor/ui/UnitForm.tsx`:
- Around line 167-175: The Button inside the UnitForm is rendered within a form
and currently relies on default behavior, so add an explicit type="button" to
the <Button> that uses onClick={handleAssignmentSelect} (the button reading “문제
연결”) to prevent accidental form submission when mode !== 'editing'; update the
JSX for that Button component to include type="button" while keeping
color='tonal', size='compact', content='mixed' and the disabled prop logic
unchanged.
---
Nitpick comments:
In `@src/entities/assignment/api/assignmentApi.ts`:
- Line 4: Replace the relative import of assignmentScheduleSchema with the
project's absolute alias import form; locate the import statement that
references assignmentScheduleSchema and change it from a "../" relative path to
the alias-based path used across the codebase (use the "@/..." alias to point to
the model/schemas module) so imports are consistent and refactor-safe.
In `@src/entities/unit/model/schemas.ts`:
- Around line 23-25: The refine check in schemas.ts currently compares
releaseDate <= dueDate as strings; change the .refine on the schema that
references releaseDate and dueDate to parse both into Date objects (e.g., via
new Date(...) or Date.parse) and compare their numeric time values
(dateA.getTime() <= dateB.getTime()), also ensure you guard against invalid
dates (isNaN) and return false (with the same message/path) when parsing fails
so invalid inputs are rejected.
ℹ️ Review info
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (8)
src/entities/assignment/api/assignmentApi.tssrc/entities/assignment/model/types.tssrc/entities/course/model/schemas.tssrc/entities/course/model/types.tssrc/entities/unit/api/unitApi.tssrc/entities/unit/model/schemas.tssrc/entities/unit/model/types.tssrc/pages/unit-editor/ui/UnitForm.tsx
🚧 Files skipped from review as they are similar to previous changes (4)
- src/entities/assignment/model/types.ts
- src/entities/course/model/schemas.ts
- src/entities/unit/api/unitApi.ts
- src/entities/unit/model/types.ts
| <Button | ||
| onClick={handleAssignmentSelect} | ||
| color='tonal' | ||
| size='compact' | ||
| content='mixed' | ||
| disabled={mode === 'editing'}> | ||
| <AddIcon className='w-3 h-3' /> | ||
| 문제 연결 | ||
| </Button> |
There was a problem hiding this comment.
type="button" 명시 필요
<Button> 컴포넌트가 <form> 내부에 있어서, 기본 타입이 submit일 경우 클릭 시 의도치 않은 폼 제출이 발생할 수 있습니다. onClick 핸들러만 실행되도록 type="button"을 명시하세요.
🐛 수정 제안
<Button
+ type='button'
onClick={handleAssignmentSelect}
color='tonal'
size='compact'
content='mixed'
disabled={mode === 'editing'}>🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@src/pages/unit-editor/ui/UnitForm.tsx` around lines 167 - 175, The Button
inside the UnitForm is rendered within a form and currently relies on default
behavior, so add an explicit type="button" to the <Button> that uses
onClick={handleAssignmentSelect} (the button reading “문제 연결”) to prevent
accidental form submission when mode !== 'editing'; update the JSX for that
Button component to include type="button" while keeping color='tonal',
size='compact', content='mixed' and the disabled prop logic unchanged.
There was a problem hiding this comment.
Actionable comments posted: 1
🧹 Nitpick comments (1)
src/pages/select-assignment/ui/AssignmentListContainer.tsx (1)
5-12: 제네릭 계약이 흐려져 타입 결합도가 올라갔습니다
AssignmentListContainerProps<T>는 제네릭인데onSelect/handleSelect는Assignment로 고정되어 있습니다. 현재 동작은 가능해도, 타입 소스가 2갈래라 이후 변경 시 깨지기 쉽습니다. 단일 타입(AssignmentCourse['assignments'][number])으로 통일하는 편이 유지보수에 유리합니다.
참고: https://www.typescriptlang.org/docs/handbook/2/generics.html타입 통일 예시 diff
-import type {Assignment} from '@/entities/assignment/model/types'; import type {AssignmentCourse} from '@/entities/course/model/types'; import type {MouseEvent, ReactNode} from 'react'; -type T = AssignmentCourse['assignments'][number]; +type AssignmentItem = AssignmentCourse['assignments'][number]; -interface AssignmentListContainerProps<T> { - items: T[]; - renderItem: (item: T) => ReactNode; +interface AssignmentListContainerProps { + items: AssignmentItem[]; + renderItem: (item: AssignmentItem) => ReactNode; title: string; - onSelect?: (item: Assignment) => void; + onSelect?: (item: AssignmentItem) => void; } const AssignmentListContainer = ({ onSelect, items, renderItem, title, -}: AssignmentListContainerProps<T>) => { - const handleSelect = (item: Assignment, event: MouseEvent) => { +}: AssignmentListContainerProps) => { + const handleSelect = (item: AssignmentItem, event: MouseEvent<HTMLButtonElement>) => { event.stopPropagation(); onSelect?.(item); };Also applies to: 19-23
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/pages/select-assignment/ui/AssignmentListContainer.tsx` around lines 5 - 12, AssignmentListContainerProps<T> currently accepts a generic T but the onSelect/handleSelect callbacks are fixed to Assignment, creating a mixed contract; change the props and related handlers to use a single concrete type AssignmentCourse['assignments'][number] (remove/replace the generic T) so items, renderItem, onSelect, and any handleSelect function all share the same type (update AssignmentListContainerProps, the items prop, renderItem signature, and handleSelect/onSelect declarations to reference AssignmentCourse['assignments'][number] consistently).
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@src/pages/select-assignment/ui/AssignmentListContainer.tsx`:
- Around line 20-23: The list item click handler currently uses onClick directly
on the li (with handleSelect defined as (item,event: MouseEvent) => {
event.stopPropagation(); onSelect?.(item); }), which breaks keyboard
accessibility; move the interactive handler from the li to a focusable element
such as a button wrapped inside the li (or replace the li content with a
button), give the button type="button", attach the click handler (call
handleSelect or a small wrapper that calls onSelect) to that button, ensure
event.stopPropagation() is applied on the button's event, and add appropriate
keyboard/ARIA attributes (e.g., aria-selected or role if needed) so Enter/Space
activate the same behavior; update any tests/styles that assume li onClick
accordingly.
---
Nitpick comments:
In `@src/pages/select-assignment/ui/AssignmentListContainer.tsx`:
- Around line 5-12: AssignmentListContainerProps<T> currently accepts a generic
T but the onSelect/handleSelect callbacks are fixed to Assignment, creating a
mixed contract; change the props and related handlers to use a single concrete
type AssignmentCourse['assignments'][number] (remove/replace the generic T) so
items, renderItem, onSelect, and any handleSelect function all share the same
type (update AssignmentListContainerProps, the items prop, renderItem signature,
and handleSelect/onSelect declarations to reference
AssignmentCourse['assignments'][number] consistently).
ℹ️ Review info
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (3)
src/pages/course-overview/ui/UnitItem.tsxsrc/pages/select-assignment/ui/AssignmentListContainer.tsxsrc/pages/unit-editor/ui/UnitList.tsx
🚧 Files skipped from review as they are similar to previous changes (1)
- src/pages/unit-editor/ui/UnitList.tsx
| const handleSelect = (item: Assignment, event: MouseEvent) => { | ||
| event.stopPropagation(); | ||
| onSelect(id); | ||
| onSelect?.(item); | ||
| }; |
There was a problem hiding this comment.
li 직접 클릭 처리로 키보드 접근성이 깨집니다
Line 29처럼 li에 onClick을 직접 주면 키보드(Enter/Space)로 동일 동작을 보장하기 어렵습니다. 선택 가능한 항목은 button 같은 포커스 가능한 요소로 감싸는 쪽이 안전합니다.
참고: https://developer.mozilla.org/en-US/docs/Web/HTML/Element/button , https://www.w3.org/WAI/ARIA/apg/practices/keyboard-interface/
접근성 개선 예시 diff
- const handleSelect = (item: Assignment, event: MouseEvent) => {
+ const handleSelect = (item: Assignment, event: MouseEvent<HTMLButtonElement>) => {
event.stopPropagation();
onSelect?.(item);
};
@@
- <li onClick={(e) => handleSelect(item, e)} key={item.id}>
- {renderItem(item)}
- </li>
+ <li key={item.id}>
+ <button
+ type='button'
+ onClick={(e) => handleSelect(item, e)}
+ className='w-full text-left'
+ >
+ {renderItem(item)}
+ </button>
+ </li>Also applies to: 29-30
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@src/pages/select-assignment/ui/AssignmentListContainer.tsx` around lines 20 -
23, The list item click handler currently uses onClick directly on the li (with
handleSelect defined as (item,event: MouseEvent) => { event.stopPropagation();
onSelect?.(item); }), which breaks keyboard accessibility; move the interactive
handler from the li to a focusable element such as a button wrapped inside the
li (or replace the li content with a button), give the button type="button",
attach the click handler (call handleSelect or a small wrapper that calls
onSelect) to that button, ensure event.stopPropagation() is applied on the
button's event, and add appropriate keyboard/ARIA attributes (e.g.,
aria-selected or role if needed) so Enter/Space activate the same behavior;
update any tests/styles that assume li onClick accordingly.
There was a problem hiding this comment.
스키마로 옮겼으면 type.ts의 수동 타입 정의는 삭제하고 z.infer<>로 통일하면 될 것 같아요!
There was a problem hiding this comment.
얘도 마찬가지입니다! 걍 스키마 밑에 z.infer로 타입을 스키마에서 파생시키는 걸로 충분해요!
There was a problem hiding this comment.
혹시 이 파일은 어떤 상황에서 사용할 계획이신지 궁금합니다!
errors 필드가 swagger 예시 placeholder 그대로 들어가 있어서 여쭤봐요!
⚙️ Related ISSUE Number
Related #62
📄 Work Description
📷 Screenshot
💬 To Reviewers
🔗 Reference